package com.bjpowernode.config.filter;

import cn.hutool.json.JSONUtil;
import com.bjpowernode.constant.Constants;
import com.bjpowernode.entity.TUser;
import com.bjpowernode.manager.RedisManager;
import com.bjpowernode.result.CodeEnum;
import com.bjpowernode.result.R;
import com.bjpowernode.utils.JwtUtil;
import com.bjpowernode.utils.ResponseUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 *
 */
@Component
@Slf4j
public class JwtVerifyFilter extends OncePerRequestFilter {

    @Resource
    private RedisManager redisManager;

    @Resource
    private JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 设置编码
        response.setCharacterEncoding("UTF-8");
        // 获取uri
        String requestURI = request.getRequestURI();
        log.info("requestURI : {}", requestURI);
        // 判断请求
        if (!Constants.LOGIN_URL.equals(requestURI)) {
            String jwt = request.getHeader("Authorization");
            log.info("jwt : {}", jwt);

            if (!StringUtils.hasText(jwt)) {
                R r = R.FAIL(CodeEnum.USER_JWT_IS_EMPTY);
                ResponseUtil.writeResult(r, response);
            } else if (!jwtUtil.verifyJwt(jwt)) {
                R r = R.FAIL(CodeEnum.USER_JWT_IS_ILLEGALITY);
                ResponseUtil.writeResult(r, response);
            } else {
                String userJSON = jwtUtil.parseToken(jwt);
                TUser tUser = JSONUtil.toBean(userJSON, TUser.class);

                String redisJwt = (String) redisManager.getValue(Constants.REDIS_LOGIN_JWT_KEY + tUser.getId());

                if (!jwt.equals(redisJwt)) {
                    R r = R.FAIL(CodeEnum.USER_JWT_ERROR);
                    ResponseUtil.writeResult(r, response);
                } else {
                    // 对token过期时间进行刷新 , 为了不影响代码的执行效率 , 这个可以使用一个多线程处理 , 开启一个异步处理
                    Boolean rememberMe = Boolean.parseBoolean(request.getHeader("rememberMe"));
                    /*new Thread(() -> {
                        // 判断是记住我 还是 没有记住我
                        if (rememberMe) {
                            redisManager.flushKeyExpire(Constants.REDIS_LOGIN_JWT_KEY + tUser.getId(), 7, TimeUnit.DAYS);
                        } else {
                            redisManager.flushKeyExpire(Constants.REDIS_LOGIN_JWT_KEY + tUser.getId(), 30, TimeUnit.MINUTES);
                        }
                    }).start();*/
                    // 直接判断 , 自动按照异步进行执行 , 不阻塞主线程的情况下执行耗时的任务
                    if (rememberMe) {
                        redisManager.flushKeyExpire(Constants.REDIS_LOGIN_JWT_KEY + tUser.getId(), 7, TimeUnit.DAYS);
                    } else {
                        redisManager.flushKeyExpire(Constants.REDIS_LOGIN_JWT_KEY + tUser.getId(), 30, TimeUnit.MINUTES);
                    }
                    /*
                     * 第一个参数`tUser`：通常是用户的主体（如`UserDetails`对象或其任何子类）
                     * 第二个参数`tUser.getLoginPwd()`：这是用户的密码。
                     * 第三个参数`AuthorityUtils.NO_AUTHORITIES`：这是一个表示用户没有任何权限的数组
                     * */
                    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(tUser, tUser.getLoginPwd(), AuthorityUtils.NO_AUTHORITIES);
                    // 将jwt往spring security的context上下文中设置一下 , 那么spring security后面的filter就知道这个人是登录过的
                    // 自定义的登录过程中（如使用REST API进行登录）手动设置已验证的用户。
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                    filterChain.doFilter(request, response);
                }
            }
        } else {
            filterChain.doFilter(request, response);
        }
    }
}
